#include <assert.h>
#include <ft2build.h>
#include <qlib/geo/key_point.h>
#include <qlib/geo/label.h>
#include <qlib/utils/font.h>
#include FT_FREETYPE_H

namespace qlib {
std::string KeyPoint::fontPath = "C:/Windows/Fonts/Arial.ttf";
float KeyPoint::fontSizeBase = 0.02;

KeyPoint::KeyPoint() {
  rotation = 0;
  magnification = 1.0;
  x_reflection = false;

  layer = 0;
  datatype = 0;

  owner = nullptr;
  reference_number = -1;

  box = Vec2dArray(2);
}

KeyPoint::KeyPoint(std::string _text, Vec2d _origin, double _rotation, double _magnification, bool _x_reflection, uint32_t _layer,
                   uint32_t _datatype) {
  text = _text;
  rotation = _rotation;
  origin = _origin;
  magnification = _magnification;
  x_reflection = _x_reflection;
  layer = _layer;
  datatype = _datatype;

  box = Vec2dArray(2);
}

KeyPoint::KeyPoint(const KeyPoint& oth) {
  operator=(oth);
}
KeyPoint& KeyPoint::operator=(const KeyPoint& other) {
  fontSize = other.fontSize;
  text = other.text;
  origin = other.origin;
  anchor = other.anchor;
  rotation = other.rotation;
  magnification = other.magnification;
  x_reflection = other.x_reflection;
  layer = other.layer;
  datatype = other.datatype;
  repetition = other.repetition;

  properties.resize(other.properties.size());
  for (size_t i = 0; i < properties.size(); i++) {
    properties[i] = Property::create();
    if (other.properties[i]) {
      properties[i] = Property::create();
      *properties[i] = *other.properties[i];
    }
  }

  offset = other.offset;
  textSize = other.textSize;

  box = other.box;
  reference_number = other.reference_number;

  lines = other.lines;

  return *this;
}

KeyPoint::s_ptr KeyPoint::copy() const {
  auto res = KeyPoint::create();
  *res = *this;

  return res;
}

void KeyPoint::copy(qlib::Label::s_ptr label) {
  text = label->text;
  origin = label->origin;
  rotation = label->rotation;
  magnification = label->magnification;
  x_reflection = label->x_reflection;
  layer = label->layer;
  datatype = label->datatype;
  properties = label->properties;
  repetition = label->repetition;
}

void KeyPoint::apply_repetition(KeyPointPtrArray& result) {
  if (repetition.type == RepetitionType::None)
    return;

  Array<Vec2d> offsets = {};
  repetition.get_offsets(offsets);
  repetition.type = RepetitionType::None;

  // Skip first offset (0, 0)
  double* offset_p = (double*)(offsets.data() + 1);
  result.reserve(offsets.size() - 1);

  KeyPoint::s_ptr KeyPoint;
  for (uint64_t offset_count = offsets.size() - 1; offset_count > 0; offset_count--) {
    KeyPoint = copy();

    KeyPoint->origin.x += *offset_p++;
    KeyPoint->origin.y += *offset_p++;

    result.push_back(KeyPoint);
  }
}

Vec2dArray KeyPoint::bounding_box() const {
  Vec2dArray res(2);
  Vec2d& min = res[0];
  Vec2d& max = res[1];

  min.x = min.y = DBL_MAX;
  max.x = max.y = -DBL_MAX;

  auto points = get_text(text, origin, fontSize, rotation, Anchor::S);
  points.push_back(origin);
  // 计算未应用重复下的包围盒
  const Vec2d* p = points.data();
  for (uint64_t num = points.size(); num > 0; num--, p++) {
    if (p->x < min.x)
      min.x = p->x;
    if (p->x > max.x)
      max.x = p->x;
    if (p->y < min.y)
      min.y = p->y;
    if (p->y > max.y)
      max.y = p->y;
  }
  return res;
}

void KeyPoint::transform(double mag, bool x_refl, double rot, const Vec2d orig) {
  const int r1 = x_refl ? -1 : 1;
  const double crot = cos(rot);
  const double srot = sin(rot);
  const double x = origin.x;
  const double y = origin.y;
  origin.x = orig.x + mag * (x * crot - r1 * y * srot);
  origin.y = orig.y + mag * (x * srot + r1 * y * crot);
  rotation = r1 * rotation + rot;
  magnification *= mag;
  x_reflection ^= x_refl;
}

qlib::Vec2d ftVecToVec2dT(const FT_Vector& ftVec) {
  return {static_cast<double>(ftVec.x) / 1.0, static_cast<double>(ftVec.y) / 1.0};
}

void extractOutlineVerticesT(FT_GlyphSlot slot, Vec2dArray& vertices) {
  for (int i = 0; i < slot->outline.n_contours; ++i) {
    int start = (i == 0) ? 0 : slot->outline.contours[i - 1] + 1;
    int end = slot->outline.contours[i];

    for (int j = start; j <= end; ++j) {
      FT_Vector point = {slot->outline.points[j].x, slot->outline.points[j].y};
      vertices.push_back(ftVecToVec2dT(point));
    }
  }
}

std::vector<Polygon::s_ptr> KeyPoint::to_polygons() const {
  std::vector<Vec2dArray> res = getStrPoints(text, Anchor::S, origin, fontSize, rotation, FontType::Gdstk);

  std::vector<Polygon::s_ptr> resPoly(res.size());

  for (size_t i = 0; i < res.size(); i++) {
    resPoly[i] = Polygon::create();
    resPoly[i]->points = res[i];

    auto box_t = resPoly[i]->bounding_box();
    if (box_t[0].x < box[0].x) {
      box[0].x = box_t[0].x;
    }
    if (box_t[1].x > box[1].x) {
      box[1].x = box_t[1].x;
    }
    if (box_t[0].y < box[0].y) {
      box[0].y = box_t[0].y;
    }
    if (box_t[1].y > box[1].y) {
      box[1].y = box_t[1].y;
    }
  }

  auto center = box_center(box);

  for (size_t i = 0; i < res.size(); i++) {
    resPoly[i]->rotate(rotation, center);
  }

  qlib::Vec2d mirro1 = {center.x, center.y + 100000};
  qlib::Vec2d mirro2 = {center.x, center.y - 100000};

  for (size_t i = 0; i < res.size(); i++) {
    if (x_reflection) {
      resPoly[i]->mirror(mirro1, mirro2);
    }
  }

  return resPoly;
}

Vec2dArray KeyPoint::get_texts() {
  if (haveLabel) {
    return get_text(text, origin, fontSize, rotation, Anchor::S);
  } else {
    return {};
  }
}

void KeyPoint::translate(double dx, double dy) {
  origin += {dx, dy};
}

void KeyPoint::mirror(qlib::Vec2d p0, qlib::Vec2d p1) {
  Vec2d v = p1 - p0;
  double tmp = v.length_sq();
  if (tmp == 0)
    return;

  Vec2d r = v * (2 / tmp);
  Vec2d p2 = p0 * 2;

  Vec2d* p = &origin;
  *p = v * (*p - p0).inner(r) - *p + p2;

  x_reflection = !x_reflection;

  // rotation = 3.14 - rotation;
}

void KeyPoint::rotate(double angle, Vec2d _center) {
  rotation += angle;

  origin -= _center;
  auto t = origin;

  double ca = cos(angle);
  double sa = sin(angle);

  origin.x = t.x * ca - t.y * sa + _center.x;
  origin.y = t.x * sa + t.y * ca + _center.y;
}

KeyPointParts KeyPoint::get_parts() {
  auto res = std::make_tuple(origin);
  return res;
}

Vec2dArray KeyPoint::query_parts(GeoPartType type, uint32_t id) {
  auto parts = get_parts();
  if (type == GeoPartType::Center)
    return Vec2dArray{std::get<0>(parts)};
  return Vec2dArray{};
}

Vec2dArray KeyPoint::render_aabb() {
  return bounding_box();
}

std::vector<Vec2dArray> KeyPoint::render_poly() {
  std::vector<Vec2dArray> res;
  auto polys = to_polygons();
  for (auto p : polys) {
    res.push_back(p->points);

    Vec2dArray box_t = p->bounding_box();
    if (box_t[0].x < box[0].x) {
      box[0].x = box_t[0].x;
    }
    if (box_t[0].y < box[0].y) {
      box[0].y = box_t[0].y;
    }
    if (box_t[1].x > box[1].x) {
      box[1].x = box_t[1].x;
    }
    if (box_t[1].y > box[1].y) {
      box[1].y = box_t[1].y;
    }
  }
  return res;
}

Vec2dArray KeyPoint::render_absorp() {
  return Vec2dArray{origin};
}

void KeyPoint::saveToProper() {
  // Property::s_ptr property = Property::create();
  // property->name = s_gds_property_name;
  // property->reference_number = 0;

  // auto prop1 = PropertyValue::create();
  // prop1->type = PropertyType::UnsignedInteger;
  // prop1->integer = 0;

  // property->values.push_back(prop1);

  // auto prop2 = PropertyValue::create();
  // prop2->type = PropertyType::String;
  // prop2->string = std::to_string(fontSize);

  // property->values.push_back(prop2);

  // properties.push_back(property);
}

void KeyPoint::readProper() {
  // fontSize = std::stod(properties[0]->values[1]->string);
}

double KeyPoint::getRotation() {
  return rotation;
}

void KeyPoint::setRotation(double _rotation) {
  rotation = _rotation;
}

void KeyPoint::savePropertiesGDS() {
  set_gds_property(properties, 0, std::to_string(fontSize));  // 字体大小
  set_gds_property(properties, 1, "keyPoint");                // 是否为关键点
  set_gds_property(properties, 3, "");                        // 是否为引脚

  set_gds_property(properties, 11, (haveLabel ? "Have" : "NoHave"));  // 是否为引脚
}

void KeyPoint::loadPropertiesGDS() {
  if (get_gds_property(properties, 0)) {
    std::string fontSizeS = (*get_gds_property(properties, 0))[1]->string;
    fontSize = std::stod(fontSizeS);
  }

  if (get_gds_property(properties, 11)) {
    std::string haveLabelS = (*get_gds_property(properties, 11))[1]->string;
    haveLabel = haveLabelS == "Have";
  }
}

}  // namespace qlib